﻿using System;
using System.Collections.Generic;
using BizElements.Core;

namespace BizElements.BusinessLayer
{
    /// <summary>Manages journal records in database.</summary>
    /// <typeparam name="TSourceEntity">Source table entity class.</typeparam>
    /// <typeparam name="TJournalEntity">Journal table entity class.</typeparam>
    public class EntityJournalManager<TSourceEntity, TJournalEntity>
        where TSourceEntity : IEntity, new()
        where TJournalEntity : IEntity, new()
    {
        #region Members.

        IJournalEntityBuilder<TSourceEntity, TJournalEntity> journalEntityBuilder;

        #endregion

        #region CTor and Create* methods.

        /// <summary>Initializes a new instance of EntityJournalManager class.</summary>
        /// <param name="journalEntityBuilder">Journal entity builder.</param>
        public EntityJournalManager(IJournalEntityBuilder<TSourceEntity, TJournalEntity> journalEntityBuilder)
        {
            if (journalEntityBuilder == null)
                throw new ArgumentNullException("journalEntityBuilder");

            this.journalEntityBuilder = journalEntityBuilder;
        }

        /// <summary>Initializes a new instance of EntityJournalManager class.</summary>
        /// <param name="buildJournalEntityFromSource">A delegate to a method that creates a journal record.</param>
        public EntityJournalManager(Delegates.Function<TJournalEntity, TSourceEntity> buildJournalEntityFromSource)
        {
            if (buildJournalEntityFromSource == null)
                throw new ArgumentNullException("buildJournalEntityFromSource");

            this.journalEntityBuilder = new CustomJournalEntityBuilder<TSourceEntity, TJournalEntity>(buildJournalEntityFromSource);
        }

        /// <summary>Automatically creates a manager that uses generated table meta-data to populate journal records.</summary>
        /// <param name="journalTimeStampField">Optional time-stamp field in journal table.</param>
        /// <returns>An instance of EntityJournalManager class.</returns>
        public static EntityJournalManager<TSourceEntity, TJournalEntity> CreateWithDefaultSettings(IDbColumn journalTimeStampField)
        {
            return CreateWithDefaultSettings(journalTimeStampField, TimeStampPrecision.Maximum);
        }

        /// <summary>Automatically creates a manager that uses generated table meta-data to populate journal records.</summary>
        /// <param name="journalTimeStampField">Optional time-stamp field in journal table.</param>
        /// <param name="journalTimeStampPrecision">Precision of optional time-stamp field in journal table.</param>
        /// <returns>An instance of EntityJournalManager class.</returns>
        public static EntityJournalManager<TSourceEntity, TJournalEntity> CreateWithDefaultSettings(IDbColumn journalTimeStampField, TimeStampPrecision journalTimeStampPrecision)
        {
            var jnRecordBuilder = new JournalEntityBuilder<TSourceEntity, TJournalEntity>();
            jnRecordBuilder.CreateDefaultPropertyMap();
            if (journalTimeStampField != null)
            {
                jnRecordBuilder.JournalTimeStampProperty = journalTimeStampField.PropertyName;
                jnRecordBuilder.JournalTimeStampPrecision = journalTimeStampPrecision;
            }

            return new EntityJournalManager<TSourceEntity, TJournalEntity>(jnRecordBuilder);
        }

        #endregion

        /// <summary>Writes to entity journal.</summary>
        /// <param name="entity">Entity to be logged.</param>
        /// <param name="conn">Optional connection-transaction context.</param>
        public void SaveToJournal(TSourceEntity entity, IConnectionProvider conn)
        {
            var journalRecord = this.journalEntityBuilder.BuildJournalEntity(entity);
            DAL.GetDao(journalRecord, conn).InsertOne();
        }

        /// <summary>Writes to entity journal.</summary>
        /// <param name="entities">Collection of entities to be logged.</param>
        /// <param name="conn">Optional connection-transaction context.</param>
        public void SaveToJournal(IEnumerable<TSourceEntity> entities, IConnectionProvider conn)
        {
            var journalRecords = this.journalEntityBuilder.BuildJournalEntityCollection(entities);
            DAL.GetDao(journalRecords.IDbTable, conn).SaveCollection(journalRecords);
        }
    }
}
